package Dominio.Entidades;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

public class Partida implements Serializable {

    // Atributos
    private Jugador blanco;
    private Jugador negro;
    private Celda[][] tablero;
    private int cantidadMaximaJugadas;
    private int cantidadC;
    private int cantidadR;
    private int cantidadL;
    private ArrayList<Jugada> listaJugadas;
    private Jugador ganador;
    private Jugador abandona;

    //Contructor con parametros
    public Partida(Jugador pBlanco, Jugador pNegro, Celda[][] pTablero, int pCantidadL, int pCantidadMaximaJugadas) {
        this.blanco = pBlanco;
        this.negro = pNegro;
        this.tablero = pTablero;
        this.cantidadMaximaJugadas = pCantidadMaximaJugadas;
        this.cantidadC = 2;
        this.cantidadR = 1;
        this.cantidadL = pCantidadL;
        this.listaJugadas = new ArrayList<Jugada>();
    }

    /*  Comienzo de bloque de propiedades GET y SET  */
    public Jugador getBlanco() {
        return blanco;
    }

    public void setBlanco(Jugador pBlanco) {
        this.blanco = pBlanco;
    }

    public Jugador getNegro() {
        return negro;
    }

    public void setNegro(Jugador pNegro) {
        this.negro = pNegro;
    }

    public Celda[][] getTablero() {
        return tablero;
    }

    public void setTablero(Celda[][] pTablero) {
        this.tablero = pTablero;
    }

    public int getCantidadMaximaJugadas() {
        return cantidadMaximaJugadas;
    }

    public void setCantidadMaximaJugadas(int pCantidadMaximaJugadas) {
        this.cantidadMaximaJugadas = pCantidadMaximaJugadas;
    }

    public int getCantidadC() {
        return cantidadC;
    }

    public void setCantidadC(int pCantidadC) {
        this.cantidadC = pCantidadC;
    }

    public int getCantidadR() {
        return cantidadR;
    }

    public void setCantidadR(int pCantidadR) {
        this.cantidadR = pCantidadR;
    }

    public int getCantidadL() {
        return cantidadL;
    }

    public void setCantidadL(int pCantidadL) {
        this.cantidadL = pCantidadL;
    }

    public Jugador getGanador() {
        return ganador;
    }

    public void setGanador(Jugador pGanador) {
        this.ganador = pGanador;
    }

    public ArrayList<Jugada> getListaJugadas() {
        return this.listaJugadas;
    }

    public void setListaJugadas(ArrayList<Jugada> pListaJugadas) {
        this.listaJugadas = pListaJugadas;
    }

    public Jugador getAbandona() {
        return abandona;
    }

    public void setAbandona(Jugador pAbandona) {
        this.abandona = pAbandona;
    }

    /**
     * Además de agregar la jugada a la lista de partida se ingresa en el
     * tablero
     *
     * @param pJugada
     */
    public void agregarJugada(Jugada pJugada) throws Exception {
        if (pJugada.getPieza().isEsFicha()) {
            pJugada.getPieza().setFicha(textoFichaJugador());
        }
        this.getListaJugadas().add(pJugada);
        cargarJugadasEnTablero();
    }

    /**
     *
     * @throws Exception
     */
    public void cargarJugadasEnTablero() throws Exception {
        boolean estaBlanca = false;
        boolean estaNegra = false;
        try {
            Jugada fichaBlanca = ultimaJugadaDeSuFicha(getBlanco());
            Jugada fichaNegra = ultimaJugadaDeSuFicha(getNegro());
            Collections.sort(this.getListaJugadas());
            int dimencion = this.getTablero().length;
            this.setTablero(new Celda[dimencion][dimencion]);
            for (Jugada jugada : this.getListaJugadas()) {
                if (jugada.isIngresa()) {
                    if (!jugada.getPieza().getTipo().equals(TipoPieza.Ficha)) {
                        cargarJugadaEnTablero(jugada);
                    }
                }
            }
            cargarJugadaEnTablero(fichaBlanca);
            cargarJugadaEnTablero(fichaNegra);
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     *
     * @param pJugada
     * @throws Exception
     */
    private void cargarJugadaEnTablero(Jugada pJugada) throws Exception {
        //Si la celda en tiene datos
        try {
            if (pJugada != null && pJugada.isIngresa()) {
                for (Coordenada coorde : pJugada.getPieza().getUbicacion()) {

                    if (this.getTablero()[coorde.getPosFila()][coorde.getPosColumna()] == null) {
                        cargoCeldaTablero(pJugada, coorde);
                    } else {
                        if (this.getTablero()[coorde.getPosFila()][coorde.getPosColumna()].getNivel() < pJugada.getNivel()
                                || (pJugada.getPieza().getTipo().equals(TipoPieza.Ficha))) {
                            cargoCeldaTablero(pJugada, coorde);
                        }
                    }
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     *
     * @param pJugada
     * @param pCoordenadaActual
     * @throws Exception
     */
    private void cargoCeldaTablero(Jugada pJugada, Coordenada pCoordenadaActual) throws Exception {
        boolean top = false;
        boolean left = false;
        boolean bottom = false;
        boolean right = false;
        boolean ladrillo = true;
        try {
            if (pJugada.getPieza().getTipo().equals(TipoPieza.Ficha)) {
                if (this.getTablero()[pCoordenadaActual.getPosFila()][pCoordenadaActual.getPosColumna()] == null) {
                    ladrillo = false;
                    this.getTablero()[pCoordenadaActual.getPosFila()][pCoordenadaActual.getPosColumna()] = new Celda(top, left, pJugada.getNivel(), pJugada.getPieza().getFicha(), false);
                } else {
                    this.getTablero()[pCoordenadaActual.getPosFila()][pCoordenadaActual.getPosColumna()].setFicha(pJugada.getPieza().getFicha());
                }
            } else if (pJugada.getPieza().getTipo().equals(TipoPieza.Cuadrado)) {
                this.getTablero()[pCoordenadaActual.getPosFila()][pCoordenadaActual.getPosColumna()] = new Celda(top, left, pJugada.getNivel(), null, ladrillo);

            } else {
                ladrillo = true;
                top = false;
                left = false;
                bottom = false;
                right = false;
                for (int i = 0; i < pJugada.getPieza().getUbicacion().length; i++) {
                    Coordenada buscada = pJugada.getPieza().getUbicacion()[i];
                    if (!top) {
                        top = pCoordenadaActual.esLinderaTOP(buscada);
                    }
                    if (!left) {
                        left = pCoordenadaActual.esLinderaLEFT(buscada);
                    }
                    if (!bottom) {
                        bottom = pCoordenadaActual.esLinderaBOTTOM(buscada);
                    }
                    if (!right) {
                        right = pCoordenadaActual.esLinderaRIGHT(buscada);
                    }
                }
                this.getTablero()[pCoordenadaActual.getPosFila()][pCoordenadaActual.getPosColumna()] =
                        new Celda(top, left, bottom, right, pJugada.getNivel(), null, ladrillo);
            }
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * Si el nivel para una posicion es la mayor lo retorna
     *
     * @param pJugada
     * @return
     */
    public int calcularNivelIngreso(Jugada pJugada) throws Exception {
        int nivelAnterior = 0;
        boolean encontro = false;
        try {
            //Ordeno la lista por niveles de mayor a menor
            Collections.sort(this.getListaJugadas());

            for (Iterator<Jugada> it = this.getListaJugadas().iterator(); it.hasNext() && !encontro;) {
                Jugada jugada = it.next();

                //Si la jugada no es igual a la jugada ingresada
                //entonces busco si en el nivel anterior
                //if (!jugada.getPieza().equals(pJugada.getPieza())) {

                //Si las coordenadas estan en alguna jugada
                for (int i = 0; i < pJugada.getPieza().getUbicacion().length && !encontro; i++) {
                    Coordenada posBuscada = pJugada.getPieza().getUbicacion()[i];

                    for (int j = 0; j < jugada.getPieza().getUbicacion().length && !encontro; j++) {
                        Coordenada pos = jugada.getPieza().getUbicacion()[j];

                        if (pos.equals(posBuscada) && nivelAnterior < jugada.getNivel()) {
                            nivelAnterior = jugada.getNivel();
                            encontro = true;
                        }
                    }
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return nivelAnterior;
    }

    /**
     * Si es el primer ladrillo en la partida.
     *
     * @param pJugada
     * @return
     *
     */
    public boolean primerLadrillo(Jugada pJugada) throws Exception {
        boolean retorno = false;
        int cont = 0;
        try {
            if (this.getListaJugadas() != null) {
                for (int i = 0; i < this.getListaJugadas().size(); i++) {
                    Jugada unaJugada = this.getListaJugadas().get(i);
                    if (!unaJugada.getPieza().isEsFicha() && unaJugada.getNivel() == pJugada.getNivel()) {
                        cont++;
                    }
                }
            }
            if (cont == 0) {
                retorno = true;
            }
        } catch (Exception ex) {
            throw ex;
        }
        return retorno;
    }

    /**
     * Verifica si el jugador puede continuar con la partida.
     *
     * @return
     */
    public void definirGanador(Jugada pJugadaBlanca, Jugada pJugadaNegra) throws Exception {
        boolean finalizo = false;
        try {
            //Si los dos no tinen la ficha en el tablero es empate
            if (pJugadaBlanca == null && pJugadaNegra == null) {
                //throw new Exception("El juego a terminado en empate.");
                finalizo = true;
            } else {
                if (pJugadaBlanca != null && pJugadaNegra != null) {
                    if (!pJugadaBlanca.isIngresa() && !pJugadaNegra.isIngresa()) {
                        //throw new Exception("El juego a terminado en empate.");
                        finalizo = true;
                    } //Gana Negro
                    //Si el blanco retiro su ficha y Negro la tiene ingresada
                    else if (!pJugadaBlanca.isIngresa()) {
                        pJugadaNegra.getJugador().setRanking(pJugadaNegra.getJugador().getRanking() + 1);
                        this.setGanador(this.getNegro());
                        //throw new Exception(String.format("El jugador Negro '%s' ha gando la partida.", jugadaNegra.getJugador().getAlias()));
                        finalizo = true;
                    } //Gana Blanco
                    //Si el blanco tiene ingresada su ficha y Negro la retiro
                    else if (!pJugadaNegra.isIngresa()) {
                        pJugadaBlanca.getJugador().setRanking(pJugadaBlanca.getJugador().getRanking() + 1);
                        this.setGanador(this.getBlanco());
                        //throw new Exception(String.format("El jugador Blanco '%s' ha gando la partida.", jugadaBlanca.getJugador().getAlias()));
                        finalizo = true;
                    } //Gana Blanco
                    //Si el blanco tiene ingresada su ficha en un nivel mas alto que la ficha de Negro
                    else if (pJugadaBlanca.getNivel() > pJugadaNegra.getNivel()) {
                        pJugadaBlanca.getJugador().setRanking(pJugadaBlanca.getJugador().getRanking() + 1);
                        this.setGanador(this.getBlanco());
                        //throw new Exception(String.format("El jugador Blanco '%s' ha gando la partida.", jugadaBlanca.getJugador().getAlias()));
                        finalizo = true;
                    } //Gana Negro
                    //Si el negro tiene ingresada su ficha en un nivel mas alto que la ficha de Blanco
                    else if (pJugadaBlanca.getNivel() < pJugadaNegra.getNivel()) {
                        pJugadaNegra.getJugador().setRanking(pJugadaNegra.getJugador().getRanking() + 1);
                        this.setGanador(this.getNegro());
                        //throw new Exception(String.format("El jugador Negro '%s' ha gando la partida.", jugadaNegra.getJugador().getAlias()));
                        finalizo = true;
                    } else {
                        finalizo = true;
                    }
                } //Gana Negro
                //Si Blanco no jugo su ficha
                else if (pJugadaBlanca == null) {
                    pJugadaNegra.getJugador().setRanking(pJugadaNegra.getJugador().getRanking() + 1);
                    this.setGanador(this.getNegro());
                    //throw new Exception(String.format("El jugador Negro '%s' ha gando la partida.", jugadaNegra.getJugador().getAlias()));
                    finalizo = true;
                } //Gana Blanco
                //Si Negro no jugo su ficha
                else if (pJugadaNegra == null) {
                    pJugadaBlanca.getJugador().setRanking(pJugadaBlanca.getJugador().getRanking() + 1);
                    this.setGanador(this.getBlanco());
                    //throw new Exception(String.format("El jugador Blanco '%s' ha gando la partida.", jugadaBlanca.getJugador().getAlias()));
                    finalizo = true;
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        //return finalizo;
    }

    /**
     * Determina si la partida puede continuar y define un ganador si la partida
     * a finalizado. Si se pasa el parametor finalizar en true es porque se
     * agoto el tiempo.
     *
     * @param pFinalizar
     * @return
     * @throws Exception
     */
    public boolean finalDeLaPartida(boolean pFinalizar) throws Exception {
        boolean finalizo = false;
        try {
            Jugada ultJugadaFichaBlanca = ultimaJugadaDeSuFicha(this.getBlanco());
            Jugada ultJugadaFichaNegra = ultimaJugadaDeSuFicha(this.getNegro());


            //Si no puede reslizar mas movimientos gana el adversario si tiene su ficha jugada

            if ((cuantosCuadrados(turnoJugador()) == 0 && cuantosRectangulos(turnoJugador()) == 0
                    && cuantosLadrillosL(turnoJugador()) == 0)){
                   // && !quedanMovimientosDeFicha(ultJugadaFichaBlanca) || !quedanMovimientosDeFicha(ultJugadaFichaNegra)) {

//            if ((   cuantosCuadrados(turnoJugador()) == 0 && cuantosRectangulos(turnoJugador()) == 0
//                    && cuantosLadrillosL(turnoJugador()) == 0)
//                    || !quedanMovimientosDeFicha(ultJugadaFichaBlanca) || !quedanMovimientosDeFicha(ultJugadaFichaNegra)) {

            if (!quedanMovimientosDeFicha(ultJugadaFichaBlanca) && !quedanMovimientosDeFicha(ultJugadaFichaNegra)) {

                //Ver si puede jugar la ficha
                finalizo = true;
            } else {
                if (this.turnoJugador().equals(this.getBlanco())) {

                    finalizo = !quedanMovimientosDeFicha(ultJugadaFichaBlanca);
                } else {

                    finalizo = !quedanMovimientosDeFicha(ultJugadaFichaNegra);
                }
            }
            }
            if (pFinalizar || finalizo || maximoDeJugadas()) {
                definirGanador(ultJugadaFichaBlanca, ultJugadaFichaNegra);
                finalizo = true;
            }
            
        } catch (Exception ex) {
            throw ex;
        }
        return finalizo;
    }

    /**
     *
     * @return
     */
    public String resultadoDeLaPartida() {
        String resultado = null;
        if (this.getGanador() != null) {
            resultado = String.format("El jugador Blanco '%s' ha gando la partida.", this.getGanador().getAlias());
            if (this.getGanador().equals(this.getNegro())) {
                resultado = String.format("El jugador Negro '%s' ha gando la partida.", this.getGanador().getAlias());
            }
        } else {
            resultado = "El juego a terminado en empate.";
        }
        return resultado;
    }

    /**
     * Verifico que en el nivel que estoy ingresando el ladrillo exista una base
     * de ladrillos diferentes al jugado.
     *
     * @param pJugada
     * @return
     */
    public boolean validarBaseIngresoLadrillo(Jugada pJugada) throws Exception {
        boolean esValido = true;
        boolean tieneBase = false;
        try {
            if (pJugada.getNivel() > 1) {

                Collections.sort(this.getListaJugadas(), new OrdenadorJugada());

                //Si las coordenadas estan en alguna jugada
                for (int i = 0; i < pJugada.getPieza().getUbicacion().length && esValido; i++) {
                    Coordenada posBuscada = pJugada.getPieza().getUbicacion()[i];

                    tieneBase = false;
                    for (Iterator<Jugada> it = this.getListaJugadas().iterator(); it.hasNext() && !tieneBase;) {
                        Jugada jugada = it.next();

                        //Si es una pieza en la misma ubicacion y esta en el nivel anterior
                        if (pJugada.getPieza().equals(jugada.getPieza()) && jugada.getNivel() == (pJugada.getNivel() - 1)) {
                            throw new Exception("No se puede ingresar un ladrillo sobre otro igual.");
                        } else {
                            //Si la pieza contiene la posicion buscada
                            //Si la jugada buscada esta del nivel anterior
                            if (jugada.getPieza().contienePosicion(posBuscada) && jugada.getNivel() == (pJugada.getNivel() - 1)) {
                                //Indico que encontre una pieza en la posicion buscada
                                tieneBase = true;
                            }
                        }
                    }
                    if (!tieneBase) {
                        esValido = false;
                    }
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return esValido;
    }

    /**
     * Verifica que si es una pieza la ingresada no este sobre una ficha
     *
     * @param pJugada
     * @return
     */
    public boolean validarPiezaNoSobreFicha(Jugada pJugada) throws Exception {
        boolean retorno = true;
        try {
            Jugada fichaJugador = ultimaJugadaDeSuFicha(pJugada.getJugador());
            Jugada fichaContrario = ultimaJugadaDeFichaContraria(pJugada.getJugador());

            for (int posMov = 0; posMov < pJugada.getMovimientos().length && retorno; posMov++) {
                Coordenada coordenadaBuscada = pJugada.getMovimientos()[posMov];

                //Recorremos las ubicaciones del la pieza a ingresar
                if (fichaJugador != null && fichaJugador.isIngresa() && fichaJugador.getPieza().contienePosicion(coordenadaBuscada)) {
                    retorno = false; // si el ladrillo esta sobre una ficha devuelve falso
                }
                if (fichaContrario != null && fichaContrario.isIngresa() && fichaContrario.getPieza().contienePosicion(coordenadaBuscada)) {
                    retorno = false; // si el ladrillo esta sobre una ficha devuelve falso
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return retorno;
    }

    /**
     * Verifica que si es una pieza la ingresada no este sobre una ficha
     *
     * @param pJugada
     * @return
     */
    public boolean validarNoPasarSobreFichaContraria(Jugada pJugada) throws Exception {
        boolean retorno = true;
        try {

            if (this.getListaJugadas().size() > 0) {

                Jugada ultimaContrario = ultimaJugadaDeFichaContraria(pJugada.getJugador());
                if (ultimaContrario.isIngresa() && pJugada.contienePosicion(ultimaContrario.getPieza().getUbicacion()[0])) {
                    retorno = false;
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return retorno;
    }

    /**
     * Verifica si los movimientos son ortogonales consecutivos a1b2b3c3
     *
     * @param pJugada
     * @return
     */
    public boolean verificarMovimientosOrtogonales(Jugada pJugada) throws Exception {
        boolean valida = true;
        Coordenada coordenadaAnterior = null;
        try {
            if (pJugada.getMovimientos() != null) {
                for (int i = 0; i < pJugada.getMovimientos().length && valida; i++) {
                    Coordenada coordenada = pJugada.getMovimientos()[i];

                    if (coordenadaAnterior == null) {
                        coordenadaAnterior = coordenada;
                    } else {
                        if (!((coordenadaAnterior.getPosFila() == coordenada.getPosFila()
                                && (coordenadaAnterior.getPosColumna() == coordenada.getPosColumna() + 1
                                || coordenadaAnterior.getPosColumna() == coordenada.getPosColumna() - 1))
                                || (coordenadaAnterior.getPosColumna() == coordenada.getPosColumna()
                                && ((coordenadaAnterior.getPosFila() == coordenada.getPosFila() + 1
                                || coordenadaAnterior.getPosFila() == coordenada.getPosFila() - 1))))) {

                            valida = false;
                        } else {
                            coordenadaAnterior = coordenada;
                        }
                    }
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return valida;
    }

    /**
     *
     * @param pJugada
     * @param pUltimoNivel
     * @return
     * @throws Exception
     */
    public boolean movimientosAscDes(Jugada pJugada, int pUltimoNivel) throws Exception {
        boolean valida = true;
        boolean encontro = false;
        int ultimoNivelASC = pUltimoNivel + 1;
        int ultimoNivelDES = pUltimoNivel - 1;
        try {
            if (this.getListaJugadas() != null && this.getListaJugadas().size() > 0) {
                //Ordeno las jugadas por nivel en froma descendente
                Collections.sort(this.getListaJugadas(), new OrdenadorJugada());

                for (int i = 0; i < pJugada.getMovimientos().length && valida; i++) {
                    Coordenada coordenada = pJugada.getMovimientos()[i];

                    encontro = false;
                    for (int pos = 0; pos < this.getListaJugadas().size() && valida && !encontro; pos++) {
                        Jugada jugada = this.getListaJugadas().get(pos);

                        //Si la posicion buscada para la ficha esta dentro de la jugada
                        if (jugada.getPieza().contienePosicion(coordenada)
                                && !jugada.getPieza().getTipo().equals(TipoPieza.Ficha)) {

                            if (ultimoNivelASC == jugada.getNivel() || ultimoNivelDES == jugada.getNivel()) {

                                encontro = true;
                                ultimoNivelASC = jugada.getNivel() + 1;
                                ultimoNivelDES = jugada.getNivel() - 1;
                            } else {
                                if (!encontro) {
                                    valida = false;
                                }
                            }
                        }
                    }
                    //Para controlar si esta desendiendo hasta el nivel 0
                    if (!encontro && ultimoNivelDES != 0) {
                        valida = false;
                    }
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return valida;
    }

    /**
     *
     * @param pJugada
     * @return
     * @throws Exception
     */
    public boolean validarCaraConLadrillo(Jugada pJugada) throws Exception {
        boolean pegado = false;
        try {
            if (pJugada.getMovimientos() != null) {
                for (int i = 0; i < pJugada.getMovimientos().length && !pegado; i++) {
                    Coordenada posicionBuscada = (Coordenada) pJugada.getMovimientos()[i];

                    for (Iterator<Jugada> it = this.getListaJugadas().iterator(); it.hasNext() && !pegado;) {
                        Jugada jugada = it.next();

                        //Si la jugada es del mismo nivel y no es una ficha
                        if (jugada.getNivel() == pJugada.getNivel() && !jugada.getPieza().isEsFicha()) {
                            //Si la posicionBuscada existe proxima ortogonalmente en la lista de posiciones de la jugada encontrada
                            if (existeLadrilloPegado(posicionBuscada, jugada.getPieza().getUbicacion())) {
                                pegado = true;
                            }
                        }
                    }
                }
            }
            if (!pegado) {
                throw new Exception("No el ladrillo no tine inguna de sus caras contra otro ladrillo en el nivel.");
            }
        } catch (Exception ex) {
            throw ex;
        }
        return pegado;
    }

    /**
     * Si obtiene la ultima jugada en la que se involucro su ficha
     *
     * @param pJugada
     * @return
     */
    public Jugada ultimaJugadaDeSuFicha(Jugador pJugador) throws Exception {
        boolean retiroLaFicha = false;
        int ultimoIngreso = -1;
        Jugada ultimaJugada = null;
        try {
            if (this.getListaJugadas() != null) {
                for (int i = 0; i < this.getListaJugadas().size(); i++) {
                    Jugada unaJugada = this.getListaJugadas().get(i);

                    //Si existe una jugada de ficha para le jugador actual
                    //ences guardar el numero de jugada y si la retirò del tablero
                    if (unaJugada.getPieza().getTipo().equals(TipoPieza.Ficha) && pJugador.equals(unaJugada.getJugador())) {

                        if (unaJugada.getNumero() > ultimoIngreso) {
                            ultimoIngreso = unaJugada.getNumero();
                            retiroLaFicha = true;
                            ultimaJugada = unaJugada;
                        }
                    }
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return ultimaJugada;
    }

    /**
     * Si obtiene la ultima jugada en la que se involucro su ficha
     *
     * @param pJugada
     * @return
     */
    public Jugada ultimaJugadaDeFichaContraria(Jugador pJugador) throws Exception {
        boolean retiroLaFicha = false;
        int ultimoIngreso = 0;
        Jugada ultimaJugada = null;
        try {
            if (this.getListaJugadas() != null) {
                for (int i = 0; i < this.getListaJugadas().size() && !retiroLaFicha; i++) {
                    Jugada unaJugada = this.getListaJugadas().get(i);

                    //Si existe una jugada de ficha para le jugador actual
                    //ences guardar el numero de jugada y si la retirò del tablero
                    if (unaJugada.getPieza().isEsFicha() && !pJugador.equals(unaJugada.getJugador())) {

                        if (unaJugada.getNumero() > ultimoIngreso) {
                            ultimoIngreso = unaJugada.getNumero();
                            retiroLaFicha = true;
                            ultimaJugada = unaJugada;
                        }
                    }
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return ultimaJugada;
    }

    /**
     * Verifica que la posicion buscada exsita en la lista de coordenadas de la
     * pieza.
     *
     * @param pPosicionBuscada
     * @param pListaCoordenadas
     * @return
     */
    public boolean existePosicionEnLista(Coordenada pPosicionBuscada, Coordenada[] pListaCoordenadas) throws Exception {
        boolean existe = false;
        try {
            for (int i = 0; i < pListaCoordenadas.length
                    && !existe; i++) {
                Coordenada posicionBuscada = (Coordenada) pListaCoordenadas[i];
                existe = pPosicionBuscada.equals(posicionBuscada);
            }
        } catch (Exception ex) {
            throw ex;
        }
        return existe;
    }

    /**
     *
     * @param pPosicionBuscada
     * @param pListaCoordenadas
     * @return
     */
    public boolean existeLadrilloPegado(Coordenada pPosicionBuscada, Coordenada[] pListaCoordenadas) throws Exception {
        boolean valido = false;
        try {
            //Recorro la lista de coordenadas para la pieza jugada
            for (int j = 0; j < pListaCoordenadas.length && !valido; j++) {
                Coordenada posicion = pListaCoordenadas[j];

                //Si esta una fila antes a la posicion actual o una fila despues
                //o si esta una columna antes a la posicion actual o una columa despues
                if (((posicion.getPosFila() == pPosicionBuscada.getPosFila() - 1 && posicion.getPosColumna() == pPosicionBuscada.getPosColumna())
                        || (posicion.getPosFila() == pPosicionBuscada.getPosFila() + 1 && posicion.getPosColumna() == pPosicionBuscada.getPosColumna()))
                        || ((posicion.getPosFila() == pPosicionBuscada.getPosFila() && posicion.getPosColumna() == pPosicionBuscada.getPosColumna() - 1)
                        || (posicion.getPosFila() == pPosicionBuscada.getPosFila() && posicion.getPosColumna() == pPosicionBuscada.getPosColumna() + 1))) {
                    valido = true;
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return valido;
    }

    /**
     * Dependiendo de quien sea el turno si se abandona la partida perdera.
     */
    public void abandonar() {
        this.setAbandona(turnoJugador());
        this.setGanador((this.getNegro().equals(this.getAbandona())) ? this.getBlanco() : this.getNegro());
        this.getGanador().setRanking(this.getGanador().getRanking() + 1);
    }

    /**
     * Se a jugado el maximo definido de jugadas para la partida
     *
     * @return
     */
    public boolean maximoDeJugadas() {
        /* boolean finalizo=false;
         if (listaJugadas.size() / 2 == cantidadMaximaJugadas) {
         finalizo = true;
         }*/

        return (((this.getCantidadMaximaJugadas() * 2) - this.getListaJugadas().size()) <= 0);
    }

    /**
     * El jugador a jugado todos su ladrillos L
     *
     * @param pJugador
     * @return
     * @throws Exception
     */
    public boolean maximoDeLadrillosL(Jugador pJugador) throws Exception {
        int ladrillos = 0;
        ladrillos = cuantosLadrillosL(pJugador);
        return (ladrillos <= 0);
    }

    /**
     * Indica si el jugador tiene su ficha para jugar.
     *
     * @param pJugador
     * @return
     * @throws Exception
     */
    private boolean tieneFicha(Jugador pJugador) throws Exception {
        boolean tieneLaFicha = false;
        Jugada jugada = ultimaJugadaDeSuFicha(pJugador);
        if (jugada != null) {
            tieneLaFicha = jugada.isIngresa();
        }
        return !tieneLaFicha;
    }

    /**
     *
     * @param pTipo
     * @return
     * @throws Exception
     */
    public boolean tieneDisponibleIngresarPieza(TipoPieza pTipo) throws Exception {
        boolean puede = false;
        try {
            if (pTipo.equals(TipoPieza.Ficha)) {
                puede = tieneFicha(turnoJugador());
            }
            if (pTipo.equals(TipoPieza.Cuadrado)) {
                puede = cuantosCuadrados(turnoJugador()) > 0;
            }

            if (pTipo.equals(TipoPieza.Rectangulo)) {
                puede = cuantosRectangulos(turnoJugador()) > 0;
            }

            if (pTipo.equals(TipoPieza.LadrilloL)) {
                puede = cuantosLadrillosL(turnoJugador()) > 0;
            }
        } catch (Exception ex) {
            throw ex;
        }
        return puede;
    }

    /**
     * Cuenta la cantidad de ladrillos cuadrados que le quedan para jugar al
     * jugador.
     *
     * @param pJugador
     * @return
     * @throws Exception
     */
    private int cuantosCuadrados(Jugador pJugador) throws Exception {
        int retorno = 0;
        try {
            
            int largoJugada=this.getListaJugadas().size();
            if (this.getListaJugadas() != null) {
                for (Iterator<Jugada> it = this.getListaJugadas().iterator(); it.hasNext();) {
                    Jugada jugada = it.next();
                    
                    String tipoP=jugada.getPieza().getTipo().toString();
                    retorno += (jugada.getJugador().equals(pJugador) && jugada.getPieza().getTipo().equals(TipoPieza.Cuadrado)) ? 1 : 0;
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        int cant=this.getCantidadC();
        return this.getCantidadC() - retorno;
    }

    /**
     * Cuenta la cantidad de ladrillos rectangulares que le quedan para jugar al
     * jugador.
     *
     * @param pJugador
     * @return
     * @throws Exception
     */
    private int cuantosRectangulos(Jugador pJugador) throws Exception {
        int retorno = 0;
        try {
            if (this.getListaJugadas() != null) {
                for (Iterator<Jugada> it = this.getListaJugadas().iterator(); it.hasNext();) {
                    Jugada jugada = it.next();
                    retorno += (jugada.getJugador().equals(pJugador) && jugada.getPieza().getTipo().equals(TipoPieza.Rectangulo)) ? 1 : 0;
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return this.getCantidadR() - retorno;
    }

    /**
     * Cuenta la cantidad de ladrillos L que le quedan para jugar al jugador.
     *
     * @param pJugador
     * @return
     * @throws Exception
     */
    private int cuantosLadrillosL(Jugador pJugador) throws Exception {
        int retorno = 0;
        try {
            if (this.getListaJugadas() != null) {
                for (Iterator<Jugada> it = this.getListaJugadas().iterator(); it.hasNext();) {
                    Jugada jugada = it.next();
                    retorno += (jugada.getJugador().equals(pJugador) && jugada.getPieza().getTipo().equals(TipoPieza.LadrilloL)) ? 1 : 0;
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return this.getCantidadL() - retorno;
    }

    /**
     * Verifica si hay posibles jugadas para la ficha del jugador, esto es que
     * tenga alguno de los lugares ortogonales a ella en un nivel superior o
     * inferior o esten vacios o si la ficha del rival esta en alguno de los
     * lugares ortogonales en el mismo nivel.
     *
     * @param pUltimaFicha
     * @return
     * @throws Exception
     */
    public boolean quedanMovimientosDeFicha(Jugada pUltimaFicha) throws Exception {
        boolean puede = false;
        try {
            if (this.getListaJugadas() != null) {
                //Si nunca la ingreso al tablero
                if (pUltimaFicha == null) {
                    //Verificar si hay lugar en el nivel 0.
                    puede = hayLugarParaIngresarFicha(0);
                } else {
                    //Si la retiro del tablero
                    if (!pUltimaFicha.isIngresa()) {
                        //Verificar si hay lugar en el nivel que retiro su ficha.
                        puede = hayLugarParaIngresarFicha(pUltimaFicha.getNivel());
                    } else {
                        //puede = hayLugarParaMoverFicha(pUltimaFicha);
                        //if (!puede) {
                        //Si la va a mover
                        if (this.getTablero() != null) {
                            Coordenada coordenadasDeLaFicha = pUltimaFicha.getPieza().getUbicacion()[0];
                            int nivel = pUltimaFicha.getNivel();
                            //Si no tiene movimientos posibles en el la coordenada donde esta y para el nivel deonde esta
                            if (!tieneMovimientosPosiblesEnNivel(coordenadasDeLaFicha, nivel)) {

                                //Busco si tiene movimientos posibles en otra celda del nivel
                                for (int posFila = 0; posFila < this.getTablero().length && !puede; posFila++) {
                                    Celda[] celdas = this.getTablero()[posFila];

                                    for (int posColumna = 0; posColumna < celdas.length && !puede; posColumna++) {
                                        Celda celda = celdas[posColumna];
                                        Coordenada buscada = new Coordenada(posFila, posColumna);

                                        //Si la coordenada no es igual a donde esta la ficha
                                        if (!buscada.equals(coordenadasDeLaFicha)) {
                                            //Si la celda esta vacia y el nivel en que se encuentra es 0
                                            if (celda == null && nivel == 0) {

                                                puede = tieneMovimientosPosiblesEnNivel(buscada, nivel);
                                            } else {
                                                //Si la celda es un ladrillo, no tiene una ficha y esta en el mismo nivel
                                                //if (celda.getFicha() != null) {
                                                if (celda.isEsLabrillo() && celda.getFicha() == null && celda.getNivel() == nivel) {
                                                    puede = tieneMovimientosPosiblesEnNivel(buscada, nivel);
                                                }
                                                //}
                                            }
                                        }
                                    }
                                }
                            } else {

                                puede = true;
                            }
                        }
                        //}
                    }
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return puede;
    }

    /**
     * Verificar si el jugador puede ingresar o mover la ficha en el trablero.
     *
     * @param pJugador
     */
    public boolean pudeIngresarOMoverFicha() throws Exception {
        boolean puede = false;
        try {
            if (this.getListaJugadas() != null) {
                Jugada ultimaFicha = ultimaJugadaDeSuFicha(this.turnoJugador());

                //Si nunca la ingreso al tablero
                if (ultimaFicha == null) {
                    //Verificar si hay lugar en el nivel 0.
                    puede = hayLugarParaIngresarFicha(0);
                } else {
                    //Si la retiro del tablero
                    if (!ultimaFicha.isIngresa()) {
                        //Verificar si hay lugar en el nivel que retiro su ficha.
                        puede = hayLugarParaIngresarFicha(ultimaFicha.getNivel());
                    } else {
                        //puede = hayLugarParaMoverFicha(ultimaFicha);
                        Coordenada coordenadasDeLaFicha = ultimaFicha.getPieza().getUbicacion()[0];
                        int nivel = ultimaFicha.getNivel();
                        puede = tieneMovimientosPosiblesEnNivel(coordenadasDeLaFicha, nivel);
                    }
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
        return puede;
    }

    /**
     *
     * @return @throws Exception
     */
    private boolean hayLugarParaIngresarFicha(int pNivel) throws Exception {
        boolean hayLugar = false;
        try {
            if (this.getTablero() != null) {
                for (int posFila = 0; posFila < this.getTablero().length && !hayLugar; posFila++) {
                    Celda[] celdas = this.getTablero()[posFila];
                    for (int posColumna = 0; posColumna < celdas.length && !hayLugar; posColumna++) {
                        Celda celda = celdas[posColumna];
                        if (pNivel == 0) {
                            hayLugar = (celda == null);
                        } else {
                            hayLugar = (celda != null && celda.getFicha() == null && celda.getNivel() == pNivel);
                        }
                    }
                }
            } else {
                hayLugar = true;
            }
        } catch (Exception ex) {
            throw ex;
        }
        return hayLugar;
    }

    /**
     *
     * @param coordenadasDeLaFicha
     * @param pNivel
     * @return
     */
    private boolean tieneMovimientosPosiblesEnNivel(Coordenada coordenadasDeLaFicha, int pNivel) {
        boolean retorno = false;
        if (coordenadasDeLaFicha.getPosFila() - 1 >= 0) {
            Celda celdaTOP = this.getTablero()[coordenadasDeLaFicha.getPosFila() - 1][coordenadasDeLaFicha.getPosColumna()];
            if (celdaTOP == null) {
                retorno = (pNivel ==0 || pNivel - 1 == 0);
            } else if (celdaTOP.getFicha() == null && (celdaTOP.getNivel() == pNivel - 1 || celdaTOP.getNivel() == pNivel + 1)) {
                retorno = true;
            }

        }
        if (!retorno && coordenadasDeLaFicha.getPosColumna() + 1 < this.getTablero().length) {
            Celda celdaRIGHT = this.getTablero()[coordenadasDeLaFicha.getPosFila()][coordenadasDeLaFicha.getPosColumna() + 1];
            if (celdaRIGHT == null) {
                retorno = (pNivel ==0 || pNivel - 1 == 0);
            } else if (celdaRIGHT.getFicha() == null && (celdaRIGHT.getNivel() == pNivel - 1 || celdaRIGHT.getNivel() == pNivel + 1)) {
                retorno = true;
            }

        }
        if (!retorno && coordenadasDeLaFicha.getPosFila() + 1 < this.getTablero().length) {
            Celda celdaBOTTOM = this.getTablero()[coordenadasDeLaFicha.getPosFila() + 1][coordenadasDeLaFicha.getPosColumna()];
            if (celdaBOTTOM == null) {
                retorno = (pNivel ==0 || pNivel - 1 == 0);
            } else if (celdaBOTTOM.getFicha() == null && (celdaBOTTOM.getNivel() == pNivel - 1 || celdaBOTTOM.getNivel() == pNivel + 1)) {
                retorno = true;
            }

        }
        if (!retorno && coordenadasDeLaFicha.getPosColumna() - 1 >= 0) {
            Celda celdaLEFT = this.getTablero()[coordenadasDeLaFicha.getPosFila()][coordenadasDeLaFicha.getPosColumna() - 1];
            if (celdaLEFT == null) {
                retorno = (pNivel ==0 || pNivel - 1 == 0);
            } else if (celdaLEFT.getFicha() == null && (celdaLEFT.getNivel() == pNivel - 1 || celdaLEFT.getNivel() == pNivel + 1)) {
                retorno = true;
            }
        }
        return retorno;
    }

    /**
     * De acuerdo a la cantidad de jugadas que se an realizado, si es par, es el
     * turo de Negro si es impar, es el turno de Blanco
     *
     * @return
     */
    public Jugador turnoJugador() {
        //Jugador retorno = this.getBlanco()();
        return (this.getListaJugadas().size() % 2 == 0 || this.getListaJugadas().size() == 0) ? this.getBlanco() : this.getNegro();
    }

    /**
     * De acuerdo a la cantidad de jugadas que se an realizado, si es par, es el
     * turo de Negro si es impar, es el turno de Blanco
     *
     * @return
     */
    public String turnoJugadorToString() {
        //Jugador retorno = this.getBlanco()();
        return (this.getListaJugadas().size() % 2 == 0 || this.getListaJugadas().size() == 0) ? String.format("Turno del jugador Blanco '%s'.", this.getBlanco().getAlias()) : String.format("Turno del jugador Negro '%s'.", this.getNegro().getAlias());
    }

    /**
     * Retorna el texto a colocar en la ficha dependiendo del turno del jugador
     *
     * @return
     */
    public String textoFichaJugador() {
        return (this.getListaJugadas().size() % 2 == 0 || this.getListaJugadas().size() == 0) ? "B" : "N";
    }

    public String estadoPiezasJugador() throws Exception {
        String estado = "";
        try {
            if (this.turnoJugador().equals(this.getBlanco())) {
                estado = (String.format("Blanco: dispone %s Cuadrados,  %s Rectangulos, %s Ladrillo L y %s jugado su Ficha. \n",
                        cuantosCuadrados(this.getBlanco()),
                        cuantosRectangulos(this.getBlanco()),
                        cuantosLadrillosL(this.getBlanco()) == Integer.MAX_VALUE ? "infinitos" : cuantosLadrillosL(this.getBlanco()),
                        ((tieneFicha(this.getBlanco())) ? " no ha " : " ha ")));
            } else {
                estado = (String.format("Negro: dispone %s Cuadrados,  %s Rectangulos, %s Ladrillo L y %s jugado su Ficha. \n",
                        cuantosCuadrados(this.getNegro()),
                        cuantosRectangulos(this.getNegro()),
                        cuantosLadrillosL(this.getNegro()) == Integer.MAX_VALUE ? "infinitos" : cuantosLadrillosL(this.getNegro()),
                        ((tieneFicha(this.getNegro())) ? " no ha " : " ha ")));
            }
        } catch (Exception ex) {
            throw ex;
        }
        return estado;
    }

    /*Infroma de lestado de la partida
     * Se muestra para cada jugador la cantidad de piezas que disponen para jugar.
     */
    @Override
    public String toString() {
        //StringBuilder builder = new StringBuilder();
        String estado = "";
        try {
            estado = (String.format("Blanco: dispone %s Cuadrados,  %s Rectangulos, %s Ladrillo L y %s jugado su Ficha. \n",
                    cuantosCuadrados(this.getBlanco()),
                    cuantosRectangulos(this.getBlanco()),
                    cuantosLadrillosL(this.getBlanco()) == Integer.MAX_VALUE ? "infinitos" : cuantosLadrillosL(this.getBlanco()),
                    ((tieneFicha(this.getBlanco())) ? " no ha " : " ha ")));
            estado += "\n";
            estado += (String.format("Negro: dispone %s Cuadrados,  %s Rectangulos, %s Ladrillo L y %s jugado su Ficha. \n",
                    cuantosCuadrados(this.getNegro()),
                    cuantosRectangulos(this.getNegro()),
                    cuantosLadrillosL(this.getNegro()) == Integer.MAX_VALUE ? "infinitos" : cuantosLadrillosL(this.getNegro()),
                    ((tieneFicha(this.getNegro())) ? " no ha " : " ha ")));
        } catch (Exception ex) {
            estado = ex.getMessage();
        }
        return estado;
    }
}
